package com.sojson.config.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.Filter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import com.sojson.base.BaseFilter;
import com.sojson.constant.Constant;
import com.sojson.util.INI4jUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.filter.FilterUtil;
import com.sojson.util.text.Convert;

import lombok.extern.slf4j.Slf4j;

/**
 * 配置过滤器bean
 * 
 * @author liu
 * @date 2020-11-02
 */
@Slf4j
@Configuration
public class FilterConfig {

    /** 过滤器权限列表在ini文件中的分组 */
    private String permissionIniFilter =
        Convert.toStr(Constant.PROPERTIES.getProperty("so.permission.ini.filter"), "filter");
    /** 过滤器不过滤权限列表在ini文件中的分组 */
    private String permissionIniFilterExclude =
        Convert.toStr(Constant.PROPERTIES.getProperty("so.permission.ini.filterExclude"), "filter_exclude");

    /**
     * 注入跨域过滤器
     * 
     * @return
     */
    @Bean
    public FilterRegistrationBean<Filter> corsFilter(CorsFilter customCorsFilter) {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(customCorsFilter);
        // 过滤路径
        filterRegistrationBean.setUrlPatterns(getPathPatterns(FilterUtil.FILTER));
        // 设置过滤器的名称(在这里如果不加就不执行,不知道为什么,或者把CharacterEncodingFilter类的名字改成其他的也可以执行)
        filterRegistrationBean.setName("corsFilter");
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER);
        log.info("解决跨域请求的过滤器注入成功！");
        return filterRegistrationBean;
    }

    /**
     * 注入字符过滤器
     * 
     * @return
     */
    @Bean
    public FilterRegistrationBean<Filter> characterEncoding() {
        FilterRegistrationBean<Filter> filterRegistrationBean =
            new FilterRegistrationBean<>(new CharacterEncodingFilter());
        // 过滤路径
        filterRegistrationBean.setUrlPatterns(getPathPatterns(FilterUtil.FILTER));
        // 设置过滤器的名称(在这里如果不加就不执行,不知道为什么,或者把CharacterEncodingFilter类的名字改成其他的也可以执行)
        filterRegistrationBean.setName("characterEncoding");
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER);
        log.info("字符过滤器注入成功！");
        return filterRegistrationBean;
    }

    /**
     * 注入参数过滤器
     * 
     * @return
     * @throws IOException 
     */
    @Bean
    public FilterRegistrationBean<BaseFilter> parameter() throws IOException {
        ParameterFilter parameterFilter = new ParameterFilter();
        FilterRegistrationBean<BaseFilter> filterRegistrationBean = new FilterRegistrationBean<>(parameterFilter);
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER);
        log.info("参数过滤器(ParameterFilter)注入成功,过滤权限为" + parameterFilter.getName());
        return addPathPatterns(filterRegistrationBean);
    }

    /**
     * 注入参数解密过滤器
     * 
     * @return
     * @throws IOException 
     */
    @Bean
    public FilterRegistrationBean<BaseFilter> parameterDecryption() throws IOException {
        ParameterDecryptionFilter parameterDecryptionFilter = new ParameterDecryptionFilter();
        FilterRegistrationBean<BaseFilter> filterRegistrationBean =
            new FilterRegistrationBean<>(parameterDecryptionFilter);
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER + 10);
        log.info("参数解密过滤器(ParameterDecryptionFilter)注入成功,过滤权限为" + parameterDecryptionFilter.getName());
        return addPathPatterns(filterRegistrationBean);
    }

    /**
     * 注入签名验证过滤器
     * 
     * @return
     * @throws IOException 
     */
    @Bean
    public FilterRegistrationBean<BaseFilter> signatureVerification() throws IOException {
        SignatureVerificationFilter signatureVerificationFilter = new SignatureVerificationFilter();
        FilterRegistrationBean<BaseFilter> filterRegistrationBean =
            new FilterRegistrationBean<>(signatureVerificationFilter);
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER + 10);
        log.info("签名验证过滤器(SignatureVerificationFilter)注入成功,过滤权限为" + signatureVerificationFilter.getName());
        return addPathPatterns(filterRegistrationBean);
    }

    /**
     * 注入签名验证过滤器(先解密在验证)
     * 
     * @return
     * @throws IOException 
     */
    @Bean
    public FilterRegistrationBean<BaseFilter> signatureVerificationDecryption() throws IOException {
        SignatureVerificationDecryptionFilter signatureVerificationDecryptionInterceptor =
            new SignatureVerificationDecryptionFilter();
        FilterRegistrationBean<BaseFilter> filterRegistrationBean =
            new FilterRegistrationBean<>(signatureVerificationDecryptionInterceptor);
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER + 10);
        log.info("签名验证过滤器(先解密在验证)(SignatureVerificationDecryptionInterceptor)注入成功,过滤权限为"
            + signatureVerificationDecryptionInterceptor.getName());
        return addPathPatterns(filterRegistrationBean);
    }

    /**
     * 注入Xss过滤器
     * 
     * @return
     * @throws IOException 
     */
    @Bean
    public FilterRegistrationBean<BaseFilter> xss() throws IOException {
        XssFilter xssFilter = new XssFilter();
        FilterRegistrationBean<BaseFilter> filterRegistrationBean = new FilterRegistrationBean<>(xssFilter);
        // 过滤路径
        filterRegistrationBean.setUrlPatterns(getPathPatterns(FilterUtil.FILTER));
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER + 10);
        log.info("Xss过滤器(XssFilter)注入成功！");
        return addPathPatterns(filterRegistrationBean);
    }

    /**
     * 注入Token过滤器
     * 
     * @return
     * @throws IOException 
     */
    @Bean
    public FilterRegistrationBean<BaseFilter> token() throws IOException {
        TokenFilter tokenFilter = new TokenFilter();
        FilterRegistrationBean<BaseFilter> filterRegistrationBean = new FilterRegistrationBean<>(tokenFilter);
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER + 10);
        log.info("Token过滤器(TokenFilter)注入成功,过滤权限为" + tokenFilter.getName());
        return addPathPatterns(filterRegistrationBean);
    }

    /**
     * 注入WebSocket过滤器
     * 
     * @return
     */
    @Bean
    public FilterRegistrationBean<Filter> webSocketFilter() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new WebSocketFilter());
        // 过滤路径
        filterRegistrationBean.setUrlPatterns(getPathPatterns(FilterUtil.FILTER));
        // 设置过滤器的名称(在这里如果不加就不执行,不知道为什么,或者把CharacterEncodingFilter类的名字改成其他的也可以执行)
        filterRegistrationBean.setName("webSocketFilter");
        // 设置过滤器的执行顺序
        filterRegistrationBean.setOrder(FilterUtil.MIN_ORDER);
        log.info("WebSocket过滤器注入成功！");
        return filterRegistrationBean;
    }

    /**
     * 给过滤器添加拦截路径
     * 
     * @param filterRegis
     * @return
     * @throws IOException 
     */
    private FilterRegistrationBean<BaseFilter> addPathPatterns(FilterRegistrationBean<BaseFilter> filterRegis)
        throws IOException {
        INI4jUtil ini = getIni4jUtil();
        BaseFilter filter = filterRegis.getFilter();
        // 获取当前拦截器的名字
        String name = filter.getName();

        // 添加过滤权限
        String section = this.permissionIniFilter;
        LinkedList<String> path = new LinkedList<>();
        Set<Entry<String, String>> entrySet = ini.get(section).entrySet();
        for (Entry<String, String> entry : entrySet) {
            String value = entry.getValue();
            if (isNotBlank(value) && Arrays.asList(value.split(",")).contains(name)) {
                path.add(entry.getKey());
            }
        }
        if (path.size() == 0) {
            path.add("/1/2/3/4/5/6/7/8/9.abccba");
        }
        filterRegis.setUrlPatterns(path);

        // 添加不过滤权限
        HashSet<String> excludePath = new HashSet<>();
        section = this.permissionIniFilterExclude;
        entrySet = ini.get(section).entrySet();
        for (Entry<String, String> entry : entrySet) {
            String value = entry.getValue();
            if (isNotBlank(value) && Arrays.asList(value.split(",")).contains(name)) {
                excludePath.add(entry.getKey());
            }
        }
        filter.setExcludePath(excludePath);

        return filterRegis;
    }

    /**
     * 解决跨域请求的,或者用注解的方式:@CrossOrigin(origins="*",allowCredentials="true",allowedHeaders="",methods={})
     * 
     * @return
     */
    @Bean
    public CorsFilter customCorsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        /**
         * 请求常用的三种配置，*代表允许所有，当时你也可以自定义属性（比如header只能带什么,只能是post方式等等）
         */

        // 设置访问源地址
        // 你需要跨域的地址 注意这里的 127.0.0.1 != localhost,* 表示对所有的地址都可以访问
        // addAllowedOrigin("http://localhost:3030")
        corsConfiguration.addAllowedOriginPattern("*");
        // 允许的请求头
        corsConfiguration.addAllowedHeader("*");
        // 允许的请求方式 : allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
        corsConfiguration.addAllowedMethod("*");
        // 配置客户机可以缓存来自飞行前请求的响应的时间(以秒为单位)。
        // corsConfiguration.setMaxAge(3600L);
        // 配置 可以访问的地址
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }

    /**
     * 判断一个字符串是否不是空
     * 
     * @param str
     * @return
     */
    private boolean isNotBlank(String str) throws IOException {
        return StringUtil.isNotBlank(str);
    }

    /**
     * 
     * @param type 哪种过滤器
     * @return
     */
    private ArrayList<String> getPathPatterns(int type) {
        return FilterUtil.getPathPatterns(type);
    }

    /**
     * 获取权限列表文件的内容
     * 
     * @return
     * @throws IOException
     */
    private INI4jUtil getIni4jUtil() throws IOException {
        return FilterUtil.getIni4jUtil();
    }

}